package com.example.zluserservice.aspect;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.example.zluserservice.common.annotation.OperationLog;
import com.example.zluserservice.common.constant.CommonConstant;
import com.example.zluserservice.common.constant.UserCacheConstant;
import com.example.zluserservice.common.exception.BaseException;
import com.example.zluserservice.common.util.IpUtil;
import com.example.zluserservice.common.util.UserThreadLocal;
import com.example.zluserservice.dto.request.RegisterReqDTO;
import com.example.zluserservice.dto.response.UserResDTO;
import com.example.zluserservice.framework.mq.message.OperationLogMessage;
import com.example.zluserservice.framework.mq.producer.OperationLogProducer;
import com.example.zluserservice.framework.properties.JwtTokenManagerProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.websocket.SendResult;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import static org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes;

/**
 * @Author zhenliu（孙凌岳）
 * @Description AOP+ RabbitMQ实现异步发送日志
 * @Date 2025/6/20 21:53
 * @Version 1.0
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class OperationLogAspect {

    private final OperationLogProducer operationLogProducer;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Around("@annotation(operationLog),@annotation(detail)")
    public Object recordLog(ProceedingJoinPoint joinPoint, OperationLog operationLog){
        log.info("执行操作审计日志方法：{}，参数：{}", joinPoint.getSignature(), JSONUtil.toJsonStr(joinPoint.getArgs()));

        //初始化参数
        int status = CommonConstant.LOG_OPERATION_STATUS_SUCCESS;
        String detail = operationLog.detail();
        String username = "匿名用户";
        Long userId = null;
        Object result;

        try {
            //执行方法
            result = joinPoint.proceed();
            log.info("操作审计日志方法执行成功，返回值：{}", JSONUtil.toJsonStr(result));
        } catch (Throwable e) {
            status = CommonConstant.LOG_OPERATION_STATUS_FAIL;
            detail += CommonConstant.LOG_OPERATION_STATUS_FAIL_POSTFIX + "失败原因：" + e.getMessage();
            log.error("操作审计日志方法执行异常", e);
            throw new BaseException(detail);
        } finally {
            HttpServletRequest request = ((ServletRequestAttributes) currentRequestAttributes()).getRequest();
            String ip = IpUtil.getClientIp(request);
            String url = request.getRequestURI();
            String finalDetail = detail + (status == CommonConstant.LOG_OPERATION_STATUS_SUCCESS
                    ? CommonConstant.LOG_OPERATION_STATUS_SUCCESS_POSTFIX
                    : "");

            //ThreadLocal获取当前用户
            String subject = UserThreadLocal.getSubject();
            if (StrUtil.isNotBlank(subject)) {
                UserResDTO userResDTO = JSONUtil.toBean(subject, UserResDTO.class);
                if (userResDTO != null) {
                    username = userResDTO.getUsername();
                    userId = userResDTO.getId();
                }
            }

            //注册时ThreadLocal无信息，从方法参数取
            if (StrUtil.isBlank(username) || userId == null) {
                Object[] args = joinPoint.getArgs();
                for (Object arg : args) {
                    if (arg instanceof RegisterReqDTO) {
                        RegisterReqDTO dto = (RegisterReqDTO) arg;
                        if (StrUtil.isBlank(username)) {
                            username = dto.getUsername();
                        }
                    }
                }
            }

            OperationLogMessage operationLogMessage = OperationLogMessage.builder()
                    .operId(generatorLogId())
                    .userId(userId)
                    .username(username)
                    .action(operationLog.action())
                    .url(url)
                    .ip(ip)
                    .detail(finalDetail)
                    .status(status)
                    .operTime(new Date())
                    .build();

            operationLogProducer.send(operationLogMessage);
        }

        return result;
    }

    /**
     * 生成日志id 格式：{yyMMdd}{13位id}
     * @return
     */
    private Long generatorLogId() {
        //通过Redis自增序列得到序号
        Long id = stringRedisTemplate.opsForValue().increment(UserCacheConstant.OPERATION_LOG_ID_GENERATOR, 1);
        return Long.parseLong(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMdd"))) * 100000000L + id;
    }
}
